home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / VideoToolbox 96.06.15 / VideoToolboxSources / GetVoltage.c < prev    next >
C/C++ Source or Header  |  1995-05-27  |  13KB  |  337 lines

  1. /*
  2. GetVoltage.c
  3. © 1990,1991,1992 Denis G. Pelli
  4. These subroutine are for measuring voltages via the Data Translation DT2211-PGL
  5. FORERUNNER analog NuBus card. The assumed unit-gain input range is VMIN to VMAX,
  6. e.g. 0 to 5 Volts. If you change this input range (by changing the jumpers on
  7. the card) then these defined constants should be changed appropriately.
  8.  
  9. The on-board FIFO holds 1024 samples, so you can use InitiateVoltageSampling()
  10. to start the conversion, do something else, and come back later to collect the
  11. results, by calling RetrieveVoltages(). All the other routines are built upon
  12. these two.
  13.  
  14. BUGS:
  15. RetrieveVoltages() hangs up at high clock rates and high gains (e.g. 100 kHz at
  16. gain 1 or 10 kHz at gain 500). This is almost certainly some sort of hardware
  17. problem. Perhaps Data Translation could advise a work-around. What happens is
  18. that we wait forever for the Control and Status Register to indicate that there
  19. is valid data. (It checks before getting each of the n samples.) Since the clock
  20. is presumably still running the data ought to appear eventually, even if the a/d
  21. can't quite keep up. Anyway, I can do all my work at 2 kHz or lower, so it's not
  22. an issue for me.***6/23/92 This problem now occurs even at 2 kHz, but it may be
  23. an interaction with the Radius Rocket.
  24.  
  25. I don't understand why, but the THINK C profiler timing data indicates that
  26. RetrieveVoltages() is much too fast on the cases where a voltageOverflow occurs.
  27. The most likely explanation for this is some sort of glitch in the Profiler,
  28. probably an interaction with printf, so I'm ignoring it.
  29.  
  30. HISTORY:
  31. 8/8/89    dgp    written by denis,based on an early draft by Preeti
  32. 9/29/89 dgp    changed the #defines so that slot number appears in only one place.
  33. 3/28/90 dgp    call the new function CardSlot() that actually finds the ForeRunner card
  34.             on the NuBus if present, and fail gracefully if it's absent.
  35. 9/13/90    dgp    Added standard deviation to GetVoltage(). Tidied up the comments.
  36. 9/14/90    dgp    Broke up code into separate routines that initiate the sampling, and
  37.             later retrieve the samples.
  38.             Improved the error detection, handling, and reporting.
  39.             Made explicit and general the dependence on the unit-gain input voltage range.
  40.             Elaborated the main() test driver.
  41.             Sped up the key loop in RetrieveVoltages().
  42. 10/17/90 dgp removed unused variables from InitiateVoltageSampling() and
  43.             RetrieveVoltages().
  44. 5/21/91    dgp    GetVoltage() now automatically tries again at lower gain if there was
  45.             a voltage overflow. I also added the voltage limits to the diagnostic
  46.             messages.
  47. 8/24/91        dgp    Made compatible with THINK C 5.0.
  48. 6/22/92    dgp    added a brief pause between successive interrogations of the fifo when
  49.             fetching data, to prevent hang ups. I suspect that the problem
  50.             is that the fifo's input and output are not totally independent and the
  51.             excessively frequent interrogation of the output (waiting for the data
  52.             available flag to come up) prevented the fifo from accepting data.
  53. 6/23/92    dgp    well, the last fix helped but didn't solve the problem. I now explicitly
  54.             time out if the next data point doesn't appear after a reasonable wait
  55.             (100000 iterations). I modified the calling structure of GetVoltages and
  56.             RetrieveSamples to pass the number of samples by reference so that
  57.             when there is a buffer overflow or timeout the programs can proceed
  58.             using the reduced number of data actually retrieved.
  59. 10/23/92 dgp removed obsolete support for THINK C 4. Fine tuned RetrieveVoltages()
  60.             to make it as fast as possible. Removed all the beeps in the error
  61.             messages, because they annoyed me.
  62. 7/31/94    dgp changed frequency[] table from double to float, to save space.
  63. 9/5/94 dgp removed assumption in printf's that int==short.
  64. */
  65. #include "VideoToolbox.h"
  66. #define VMAX 5.0    /* unit-gain input range of a/d assumed to be VMIN to VMAX volts */
  67. #define VMIN 0.0
  68.  
  69. #define BASE_ADDR(slot)     (0xf0080000+(slot)*0x1100000)
  70. #define CS_REG         0x00
  71. #define    AD_DATA        0x10
  72. #define DAC_0_DATA    0x20
  73. #define DAC_1_DATA    0x30
  74. #define    DIO_COUNTER 0x40
  75. #define DIO_DATA    0x50
  76.  
  77. #define GAINS 4
  78. static double gain[GAINS]={1.,10.,100.,500.};
  79. #define FREQUENCIES 64
  80. static float frequency[FREQUENCIES]={
  81.     0.005,0.006,0.010,0.012,0.015,0.020,0.030,
  82.     0.050,0.060,0.060,0.100,0.120,0.150,0.200,0.300,
  83.     0.500,0.600,0.600,1.0,1.200,1.500,2.0,3.0,
  84.     5.0,6.0,6.0,10.0,12.0,15.0,20.0,30.0,
  85.     50.0,60.0,60.0,100.0,120.0,150.0,200.0,300.0,
  86.     500.0,600.0,600.0,1000.0,1200.0,1500.0,2000.0,3000.0,
  87.     5000.0,6000.0,6000.0,10000.0,12000.0,15000.0,20000.0,30000.0,
  88.     50000.0,60000.0,60000.0,100000.0,120000.0,150000.0,200000.0,300000.0,600000.0
  89. };
  90. static short frequencyCode[FREQUENCIES]={
  91.     63,15,55,47,39,31,23,62,14,7,54,46,38,30,22,
  92.     61,6,13,53,45,37,29,21,60,12,5,52,44,36,28,20,
  93.     59,11,4,51,43,35,27,19,58,3,10,50,42,34,26,18,
  94.     57,9,2,49,41,33,25,17,56,1,8,48,40,32,24,16,0
  95. };
  96.  
  97. #if 0
  98. #include <profile.h>
  99. void main(void);
  100.  
  101. void main()
  102. {
  103.     int i,k;
  104.     short channel=1;
  105.     long n=2000;
  106.     static double g=1.0,f=2000.,sd,voltDelta,voltZero,mean;
  107.     static unsigned short data[2000];
  108.     
  109.     Require(0);
  110.     for(i=0;i<3;i++)printf("%6.2f mV\n",1000.*VoltsDuringFrame(20));
  111.     InitProfile(200,3);
  112.     for(k=0;k<3;k++){
  113.         g=gain[k];
  114.         mean=GetVoltage(channel,&g,&f,n,&sd);
  115.         printf("n %ld,mean %f V,sd %f V,g %3.0f,f %.3f",n,mean,sd,g,f);
  116.         mean=GetVoltages(channel,&g,&f,n,data,&voltDelta,&voltZero);
  117.         printf(",range is %.3f to %.3f V\n",voltZero,voltDelta*4095+voltZero);
  118.         for(i=0;i<10;i++)printf("%5d",data[i]);
  119.         printf("\n");
  120.     }
  121. }
  122. #endif
  123.  
  124. double VoltsDuringFrame(double frames)
  125. /* sample channel 1 at 2 kHz for the specified number of 15 ms frames */
  126. {
  127.     double gain=10.;
  128.     double frequency=2000.;
  129.     long n;
  130.     short channel=1;
  131.     
  132.     n=(long)(0.5+frequency*0.015*frames);
  133.     return GetVoltage(channel,&gain,&frequency,n,NULL);
  134. }
  135.  
  136. double GetVoltage(short channel,double *gainPtr,double *frequencyPtr,long nDesired
  137.     ,double *sdPtr)
  138. /*
  139. Samples at specified rate, gain, and number of samples and returns the mean voltage.
  140. If sdPtr is not NULL, then the standard deviation is computed and returned in *sdPtr.
  141. */
  142. {
  143.     unsigned short *readingsPtr;
  144.     double voltDelta,voltZero,v;
  145.     int error,i;
  146.     register unsigned long sum,m;
  147.     register double sumSquares;
  148.     long n;
  149.     
  150. tryAgain:
  151.     if(nDesired >= 1L<<20){
  152.         printf("GetVoltage: n %ld too large\n",nDesired);
  153.         return NAN;
  154.     }
  155.     readingsPtr=(unsigned short *)NewPtr(nDesired*sizeof(unsigned short));
  156.     if(readingsPtr==NULL){
  157.         printf("GetVoltage: out of memory!\n");
  158.         return NAN;
  159.     }
  160.     n=nDesired;
  161.     error=GetVoltages(channel,gainPtr,frequencyPtr,&n,readingsPtr,&voltDelta,&voltZero);
  162.     if(error & voltageBufferOverflow){
  163.         printf("GetVoltage: Warning: retrieved only %4ld of the %4ld samples requested.\n",n,nDesired);
  164.     }
  165.     if(error & voltageOverflow){
  166.         if(*gainPtr>gain[0]){
  167.             printf("GetVoltage: voltageOverflow >%.2f V at gain %.0f. "
  168.                 "Trying again at lower gain.\n",VMAX/(*gainPtr),*gainPtr);
  169.             *gainPtr /=10.0;
  170.             goto tryAgain;
  171.         }
  172.         else printf("GetVoltage: voltageOverflow >%.2f V at gain %.0f.\n"
  173.             ,VMAX/(*gainPtr),*gainPtr);
  174.     }
  175.     if(error & voltageUnderflow)printf("GetVoltage: voltageUnderflow <%.3f V.\n"
  176.         ,VMIN/(*gainPtr));
  177.     sum=0;
  178.     for(i=0;i<n;i++) sum+=readingsPtr[i];
  179.     v=(double)sum/(double)n;
  180.     if(sdPtr!=NULL){
  181.         sumSquares=0.0;
  182.         for(i=0;i<n;i++){
  183.             m=readingsPtr[i];
  184.             sumSquares+=m*m;
  185.         }
  186.         *sdPtr=voltDelta*sqrt((sumSquares-n*v*v)/(n-1));
  187.     }
  188.     DisposPtr((Ptr)readingsPtr);
  189.     return voltDelta*v+voltZero;
  190. }
  191.  
  192. short GetVoltages(short channel,double *gainPtr,double *frequencyPtr,long *nPtr
  193.     ,unsigned short readings[],double *voltDeltaPtr,double *voltZeroPtr)
  194. /*
  195. Retrieves *nPtr samples from the a/d. The samples are placed in the array
  196. readings[]. The returned value of *voltDeltaPtr, when multiplied by the samples,
  197. will yield volts. The function uses a/d gain and frequency settings as close as
  198. possible to those requested, and sets the parameters to the values actually
  199. used. The returned value will be 0 when the operation was successful, and
  200. nonzero if there was an error. The error value is the OR of those that occurred:
  201. voltageBufferOverflow, voltageUnderflow, voltageOverflow. In case of buffer
  202. overflow the value of *nPtr will be reduced to the number of good samples.
  203. */
  204. {
  205.     int error=0;
  206.     
  207.     error |= InitiateVoltageSampling(channel,gainPtr,frequencyPtr,voltDeltaPtr,voltZeroPtr);
  208.     error |= RetrieveVoltages(nPtr,readings);
  209.     return error;
  210. }
  211.  
  212.  
  213. short InitiateVoltageSampling(short channel,double *gainPtr,double *frequencyPtr
  214.     ,double *voltDeltaPtr,double *voltZeroPtr)
  215. /*
  216. Initiates sampling by the a/d at specified gain and frequency (in Hz). The
  217. samples accumulate in the FIFO, which will hold 1024 before overflowing. Each
  218. sample multiplied by *voltDeltaPtr and added to *voltZeroPtr is the input
  219. voltage. This function will use gain and frequency settings as close as possible
  220. to those requested, and then set the parameters to the values actually used. The
  221. returned value is always 0.
  222. */
  223. {
  224.     register volatile short *Con_Stat_Reg;
  225.     unsigned volatile short *AD_Data_Reg;
  226.     unsigned volatile short *DIO_Counter_Reg;
  227.     register short i;
  228.     short mode;
  229.     short iGain,iFrequency;
  230.     static int slot;
  231.     
  232.     slot=ForeRunnerSlot();            /* NuBus slot of ForeRunner card */
  233.     if(slot<0){
  234.         PrintfExit("InitiateVoltageSampling: Sorry, I need a Data Translation ForeRunner A/D card.\n");
  235.     }
  236.  
  237.     AD_Data_Reg = (unsigned short *)(AD_DATA+BASE_ADDR(slot));
  238.     Con_Stat_Reg = (short *)(CS_REG+BASE_ADDR(slot));
  239.     DIO_Counter_Reg = (unsigned short *)(DIO_COUNTER+BASE_ADDR(slot));
  240.  
  241.     /* find nearest gain */
  242.     for(i=0;i<GAINS-1;i++) if(*gainPtr <= (gain[i]+gain[i+1])/2.0) break;
  243.     iGain=i;
  244.     *gainPtr=gain[iGain];
  245.     *voltDeltaPtr=(VMAX-VMIN)/4095.0/gain[iGain];
  246.     *voltZeroPtr=VMIN/gain[iGain];
  247.     
  248.     /* find nearest frequency */
  249.     for(i=0;i<FREQUENCIES-1;i++)
  250.         if(*frequencyPtr <= (frequency[i]+frequency[i+1])/2.0) break;
  251.     iFrequency=i;
  252.     *frequencyPtr=frequency[iFrequency];
  253.     
  254.     /* This follows the steps suggested in the FORERUNNER manual pages:192,194,195. */
  255.     *Con_Stat_Reg = (iGain<<6)+channel;                /* mode zero: stop */
  256.     *DIO_Counter_Reg=0;                                /* stop the clock */
  257.     while(*Con_Stat_Reg<0) i=*AD_Data_Reg;            /* empty the FIFO */
  258.     *DIO_Counter_Reg=frequencyCode[iFrequency];        /* set clock frequency */
  259.     mode=1;                                            /* enable */
  260.     *Con_Stat_Reg=(1<<12)+(mode<<8)+(iGain<<6)+channel; /* clear a/d error flag */
  261.     *AD_Data_Reg=0;                                    /* start */
  262.     return 0;
  263. }
  264.  
  265. short RetrieveVoltages(long *nPtr,unsigned short readings[])
  266. /*
  267. Retrieves *nPtr samples from the a/d. The samples are placed in the array
  268. readings[]. The returned value will be 0 when the operation was successful, and
  269. nonzero if there was an error. The error value is the OR of those that occurred:
  270. voltageBufferOverflow, voltageUnderflow, voltageOverflow. In case of buffer 
  271. overflow the value of *nPtr will be reduced to the number of good samples.
  272. */
  273. {
  274.     register volatile short *Con_Stat_Reg;
  275.     register volatile unsigned short *AD_Data_Reg;
  276.     register unsigned short *readingsPtr;
  277.     register long i,j,nDesired=*nPtr;
  278.     int slot;                        /* NuBus slot of ForeRunner card */
  279.     int error=0;
  280.     register unsigned short r;
  281.     
  282.     slot=ForeRunnerSlot();
  283.     if(slot<0){
  284.         PrintfExit("RetrieveVoltages: Sorry, I need a Data Translation ForeRunner A/D card.\n");
  285.     }
  286.  
  287.     AD_Data_Reg = (unsigned short *)(AD_DATA+BASE_ADDR(slot));
  288.     Con_Stat_Reg = (short *)(CS_REG+BASE_ADDR(slot));
  289.  
  290.     /* retrieve data as quickly as possible, before the FIFO overflows */
  291.     readingsPtr=&readings[0];
  292.     for(i=0;i<nDesired;i++) {
  293.         j=100000;
  294.         while(*Con_Stat_Reg>=0) if(--j==0)goto timeout;
  295.         *readingsPtr++ = *AD_Data_Reg;
  296.     }
  297.     if(0){
  298.     timeout:
  299. //        printf("RetrieveVoltages timed out waiting for %ld-th datum of %ld. ",i,nDesired);
  300. //        printf("*Con_Stat_Reg == 0x%X\n",*Con_Stat_Reg);
  301.         *nPtr=i;                                    /* use only the valid data */
  302.         for(;i<nDesired;i++)*readingsPtr++=0;
  303.         error|=voltageBufferOverflow;
  304.     }
  305.     *Con_Stat_Reg &= ~(3<<8);                         /* disable conversions */
  306.  
  307.     /* if we want more than the FIFO can hold, then check for FIFO overflow */
  308.     r=*Con_Stat_Reg;                // force THINK C to read a word, not a byte
  309.     if(*nPtr>1024 && r & 1<<11){
  310.         error|=voltageBufferOverflow;
  311.         *nPtr=1024;                                    /* use only the valid data */
  312.     }
  313.     while(*Con_Stat_Reg<0)i=*AD_Data_Reg;            /* now empty the FIFO */
  314.     
  315.     /* check for voltage out of range */
  316.     for(i=0;i<*nPtr;i++){
  317.         r=readings[i];
  318.         if(r==0)error|=voltageUnderflow;
  319.         if(r==4095)error|=voltageOverflow;
  320.     }
  321.     return error;
  322. }
  323.  
  324. int ForeRunnerSlot(void)
  325. /* caches the slot number to speed up subsequent calls */
  326. {
  327.     static firstTime=1;
  328.     static slot=-1;
  329.     
  330.     if(firstTime) {
  331.         slot=CardSlot(".ForeRunner");
  332.         firstTime=0;
  333.     }
  334.     return slot;
  335. }
  336.  
  337.